import * as THREE from 'three';
import React, { useRef, useEffect, useCallback } from 'react';
import { Content } from 'react-fule-ui';
import { DoubleSide } from 'three';
import './index.scss';

export default function PluginsThree() {
  const Body = useRef();
  // const Scene = new THREE.Scene(); // 这样创建的会受useStatus影响
  const Scene = useRef(new THREE.Scene()).current; // 场景
  const Camera = useRef(new THREE.PerspectiveCamera()).current; //相机
  const Reader = useRef(new THREE.WebGL1Renderer({ antialias: true })).current; // 渲染器
  const Meshs = useRef([]).current;
  const Lights = useRef([]).current;
  const Floor = useRef(); // 地板
  const IsDown = useRef(false);
  const PI = useRef(15);
  const R = useRef(90);
  const Id = useRef(null);

  // 初始化
  const init = useCallback(() => {
    // 设置canvas大小
    Reader.setSize(Body.current.offsetWidth, Body.current.offsetHeight);
    // 是否开启阴影
    Reader.shadowMap.enabled = true;
    // 设置相机参数
    Camera.aspect = Body.current.offsetWidth / Body.current.offsetHeight;
    Camera.for = 45;
    Camera.near = 1;
    Camera.far = 1000;
    Camera.position.set(0, 3, PI.current);
    Camera.lookAt(0, 0, 0);
    Camera.updateProjectionMatrix();
  }, [Reader, Camera]);
  // 创建灯光
  const creactLight = useCallback(() => {
    // 太阳光
    const dirLight = new THREE.DirectionalLight('#ffffff', 1);
    dirLight.position.set(0, 200, 0);
    dirLight.castShadow = true;
    dirLight.shadow.camera.top = -10;
    dirLight.shadow.camera.bottom = 10;
    dirLight.shadow.camera.right = -10;
    dirLight.shadow.camera.left = 10;
    dirLight.shadow.mapSize.width = 2000;
    dirLight.shadow.mapSize.height = 2000;
    // 环境光
    const ambLight = new THREE.AmbientLight('#ffffff', 0.2);
    // 点光源
    const point = new THREE.PointLight('#ffffff', 1, 16);
    point.position.set(0, 5, 0);
    //渲染
    Scene.add(dirLight, ambLight);
    Lights.push(dirLight, ambLight);
    // Scene.add(point, ambLight);
    // Lights.push(point, ambLight);
  }, [Lights, Scene]);
  //创建基础网格材质
  const createReact = useCallback(() => {
    const meshBasicMater = new THREE.MeshBasicMaterial({ color: 'red' }); // 材质
    const rect = new THREE.BoxBufferGeometry(2, 2, 2); //形状
    const mesh = new THREE.Mesh(rect, meshBasicMater); // 网格
    mesh.position.set(-6, 0, 0);
    mesh.castShadow = true;
    Scene.add(mesh);
    Meshs.push(mesh);
  }, [Meshs, Scene]);
  // 创建基础线条材质
  const createLine = useCallback(() => {
    const lineMater = new THREE.LineBasicMaterial({ vertexColors: true }); // 材质
    const geometry = new THREE.BufferGeometry();
    const color = new THREE.Color();
    // eslint-disable-next-line no-array-constructor
    let vertices = new Array();
    // eslint-disable-next-line no-array-constructor
    let color1 = new Array();
    for (let i = 0; i < 5000; i++) {
      const x = Math.random() * 2 - 1;
      const y = Math.random() * 2 - 1;
      const z = Math.random() * 2 - 1;
      // 新版本
      vertices.push(x, y, z);
      color.setHSL(Math.random(), Math.random(), Math.random());
      color1.push(color.r, color.g, color.b);

      //旧版本124
      // vertices.push(new THREE.Vector3(x, y, z));
      // geometry.vertices.push(new THREE.Vector3(x, y, z));
      // geometry.colors.push(Math.random(), Math.random(), Math.random());
    }
    //新版
    // geometry.setFromPoints(vertices);
    geometry.setAttribute(
      'position',
      new THREE.Float32BufferAttribute(vertices, 3)
    );
    geometry.setAttribute('color', new THREE.Float32BufferAttribute(color1, 3));
    //
    const mesh = new THREE.Line(geometry, lineMater); // 网格
    mesh.position.set(-2, 0, 0);
    mesh.castShadow = true;
    Scene.add(mesh);
    Meshs.push(mesh);
  }, [Meshs, Scene]);
  // 创建lambert材质
  const creactLambert = useCallback(() => {
    const lambert = new THREE.MeshLambertMaterial({ color: 'red' });
    const rect = new THREE.BoxBufferGeometry(2, 2, 2);
    const mesh = new THREE.Mesh(rect, lambert);
    mesh.position.set(2, 0, 0);
    mesh.castShadow = true;
    Scene.add(mesh);
    Meshs.push(mesh);
  }, [Meshs, Scene]);
  // 创建Phone材质
  const creactPhone = useCallback(() => {
    const phone = new THREE.MeshPhongMaterial({ color: 'red' }); // 材质
    const rect = new THREE.BoxBufferGeometry(2, 2, 2); //形状
    const mesh = new THREE.Mesh(rect, phone);
    mesh.castShadow = true;
    mesh.position.set(6, 0, 0);
    Scene.add(mesh);
    Meshs.push(mesh);
  }, [Meshs, Scene]);
  // 创建地板  - 此时可使用lambert材质
  const creatFloor = useCallback(() => {
    const lambert = new THREE.MeshLambertMaterial({
      color: '#fff',
      side: DoubleSide,
    });
    const plane = new THREE.PlaneGeometry(30, 30);
    const mesh = new THREE.Mesh(plane, lambert);
    mesh.receiveShadow = true;
    mesh.position.set(0, -4, 0);
    mesh.rotation.x = (90 * 180) / Math.PI;
    Scene.add(mesh);
    Floor.current = mesh;
  }, [Scene]);
  // 鼠标操作
  const down = useCallback(() => {
    IsDown.current = true;
  }, []);
  const up = useCallback(() => {
    IsDown.current = false;
  }, []);
  const move = useCallback(
    (e) => {
      if (IsDown.current === true) {
        R.current -= e.movementX * 0.5;
        const x = PI.current * Math.cos((R.current / 180) * Math.PI);
        const y = Camera.position.y + e.movementY * 0.2;
        const z = PI.current * Math.sin((R.current / 180) * Math.PI);
        Camera.position.set(x, y, z);
        Camera.lookAt(0, 0, 0);
      }
    },
    [Camera]
  );
  const wheel = useCallback(
    (e) => {
      if (e.deltaY > 0) {
        PI.current += 1;
      } else {
        PI.current -= 1;
      }
      const x = PI.current * Math.cos((R.current / 180) * Math.PI);
      const y = Camera.position.y;
      const z = PI.current * Math.sin((R.current / 180) * Math.PI);
      Camera.position.set(x, y, z);
      Camera.lookAt(0, 0, 0);
    },
    [Camera]
  );
  // 渲染画面
  const readerScene = useCallback(() => {
    Reader.render(Scene, Camera); //
    Meshs.forEach((item) => {
      item.rotation.x += (0.5 / 180) * Math.PI;
      item.rotation.y += (0.5 / 180) * Math.PI;
    });
    window.requestAnimationFrame(() => readerScene());
  }, [Reader, Meshs, Scene, Camera]);
  useEffect(() => {
    Body.current.append(Reader.domElement);
    init();
    creatFloor(); //
    creactLight(); //
    createReact(); //
    creactLambert(); //
    createLine(); //
    creactPhone(); //
    readerScene();
    // 销毁钩子
    return () => {
      // eslint-disable-next-line react-hooks/exhaustive-deps
      cancelAnimationFrame(Id.current);
      Meshs.forEach((item) => {
        Scene.remove(item);
        item.geometry.dispose();
        item.material.dispose();
      });
      Floor.current && Scene.remove(Floor.current);
      Lights.forEach((item) => {
        item.dispose();
      });
      Reader.dispose();
      Scene.dispose();
    };
  });

  return (
    <Content>
      <Content.Header>Three</Content.Header>
      <Content.Body full>
        <div
          className="start"
          ref={Body}
          onMouseMove={move}
          onMouseDown={down}
          onMouseUp={up}
          onWheel={wheel}
        ></div>
      </Content.Body>
    </Content>
  );
}
